/**
 * Yarbu is a proof of concept of a JavaScript HTTP server.
 * Copyright (C) 2011 Antonio Hernandez Diaz <ahdiaz@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * $Id: Shell.java 2 2011-01-08 06:03:19Z ahdiaz $
 */

package net.ahdiaz.yarbu;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Vector;

import org.mozilla.javascript.Context;
import org.mozilla.javascript.EcmaError;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;

public class Shell extends ScriptableObject {

	private static final long serialVersionUID = 4349773843486924586L;
	
	private Context cx = null;
	private Vector<String> autoloadPaths = null;
	
	@Override
	public String getClassName() {
		return "global";
	}

	public Shell() throws IOException {

		autoloadPaths = new Vector<String>();
		
		cx = Context.enter();        	
		cx.initStandardObjects(this);

		String[] names = { "load", "autoload", "readFile", "print", "basename", "basedir" };
		this.defineFunctionProperties(names, this.getClass(), ScriptableObject.DONTENUM);
		
		loadJsExtensions();
        	
//		ScriptableObject.callMethod(cx, this, "main", new Object[] { Context.javaToJS(response, this) });
//		Context.exit();
	}
	
	public Context getContext() {
		return cx;
	}
	
	public void exit() {
		Context.exit();
	}
	
	public void processSource(String filename) throws IOException {

		try {
			
			FileReader in = new FileReader(filename);
			cx.evaluateReader(this, in, filename, 1, null);
			
		} catch (EcmaError e) {
			
			e.printStackTrace();
			
//			p(e.getClass().getName());
//			p(e.getMessage());
//			p(new Integer(e.lineNumber()).toString());
//			p(e.getName());
			
			// TODO: Autoload feature
//			if (e.getName().equals("ReferenceError")) {
//			
//				Pattern pattern = Pattern.compile("ReferenceError: \"([^\"]+)\" is not defined");
//				Matcher matcher = pattern.matcher(e.getMessage());
//				
//				if (matcher.find()) {
//					p("GROUP: " + matcher.group(1));
//					String a = new File(filename).getParent() + "/%className.js".replaceAll("%className", matcher.group(1));
//					p("FILENAME: " + a);
////					processSource(a);
////					processSource(filename);
//				}
//				
//			}
		}		
	}
	
	private void loadJsExtensions() throws IOException {

		File extPath = new File(System.getProperty("user.dir") + "/jslib");
		FilenameFilter filter = new FilenameFilter() {
			
			@Override
			public boolean accept(File dir, String name) {
				return !name.startsWith(".") && name.endsWith(".js");
			}
		};
		
		String[] children = extPath.list(filter);
		if (children == null) return;
		
		for (String filename : children) {
			processSource(extPath.getAbsolutePath() + "/" + filename);
		}
	}
	
	public static void load(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
		Shell shell = (Shell) getTopLevelScope(thisObj);
		for (int i = 0; i < args.length; i++) {
			try {
				shell.processSource(Context.toString(args[i]));
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void autoload(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
		Shell shell = (Shell) getTopLevelScope(thisObj);
		for (int i = 0; i < args.length; i++) {
			String path = Context.toString(args[i]);
			shell.autoloadPaths.add(path);
		}
	}
	
	public static String readFile(Context cx, Scriptable thisObj, Object[] args, Function funObj) throws IOException {
		FileReader fr = new FileReader(Context.toString(args[0]));
		BufferedReader br = new BufferedReader(fr);
		String tmp = "";
		while (br.ready()) {
			tmp += br.readLine() + "\n";
		}
		br.close();
		return tmp;
	}
	
	public static void print(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
		for (int i=0; i < args.length; i++) {
			// Convert the arbitrary JavaScript value into a string form.
			String s = Context.toString(args[i]);
			System.out.println(s);
		}
	}
	
	public static String basename(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
		File f = new File(Context.toString(args[0]));
		return f.getName();
	}
	
	public static String basedir(Context cx, Scriptable thisObj, Object[] args, Function funObj) {
		File f = new File(Context.toString(args[0]));
		return f.getParent();
	}
	
	@SuppressWarnings("unused")
	private void p(String s) {
		System.out.println(s);
	}

}
